﻿namespace Mosquito.ViewModel.Collections {
    using System;
    using System.Collections.Generic;

    using Mosquito.ViewModel.Collections.Tree;

    public class ObservableTree<TKey, TValue> : 
        ObservableTreeBase<TKey, TKey, TValue, ObservableTree<TKey, TValue>> {
        private readonly Dictionary<TKey, ObservableTree<TKey, TValue>> immediateChildren;
        private readonly Func<TKey, IEnumerable<TKey>> splitKey;
        private readonly Func<IEnumerable<TKey>, TKey> joinKey;

        public ObservableTree(Func<TKey, IEnumerable<TKey>> splitKey, Func<IEnumerable<TKey>, TKey> joinKey) {
            this.splitKey = splitKey;
            this.joinKey = joinKey;

            this.immediateChildren = new Dictionary<TKey, ObservableTree<TKey, TValue>>();
        }

        public override IEnumerable<ObservableTree<TKey, TValue>> ImmediateChildren {
            get { return this.immediateChildren.Values; }
        }

        protected override ObservableTree<TKey, TValue> CreateNode() {
            return new ObservableTree<TKey, TValue>(this.splitKey, this.joinKey);
        }

        protected override ObservableTree<TKey, TValue> AddImmediateChild(TKey immediateKey, ObservableTree<TKey, TValue> child) {
            child.Parent = this;
            
            this.immediateChildren[immediateKey] = child;
            this.Count = this.immediateChildren.Count;

            return child;
        }

        protected override ObservableTree<TKey, TValue> GetImmediateChild(TKey immediateKey) {
            var child = default(ObservableTree<TKey, TValue>);
            
            return this.immediateChildren.TryGetValue(immediateKey, out child) ? child : null;
        }

        protected override bool RemoveImmediateChild(ObservableTree<TKey, TValue> node) {
            var removed = this.immediateChildren.Remove(node.RelativeKey);
            
            this.Count = this.immediateChildren.Count;
            
            return removed;
        }

        protected override int IndexOfKey(TKey immediateKey) {
            ObservableTree<TKey, TValue> node;
            
            if (!this.immediateChildren.TryGetValue(immediateKey, out node)) {
                return -1;
            }

            return this.IndexOfNode(node);
        }

        protected override int IndexOfNode(ObservableTree<TKey, TValue> node) {
            var idx = 0;
            foreach (var kvp in this.immediateChildren) {
                if (kvp.Value == node) {
                    return idx;
                }

                idx++;
            }

            return -1;
        }

        protected override IEnumerable<TKey> SplitKey(TKey key) {
            return this.splitKey(key);
        }

        protected override TKey JoinKey(IEnumerable<TKey> keyParts) {
            return this.joinKey(keyParts);
        }
    }
}
